/*
	FreeRTOS.org V4.2.1 - Copyright (C) 2003-2007 Richard Barry.

	This file is part of the FreeRTOS.org distribution.

	FreeRTOS.org is free software; you can redistribute it and/or modify
	it under the terms of the GNU General Public License as published by
	the Free Software Foundation; either version 2 of the License, or
	(at your option) any later version.

	FreeRTOS.org is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
	GNU General Public License for more details.

	You should have received a copy of the GNU General Public License
	along with FreeRTOS.org; if not, write to the Free Software
	Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

	A special exception to the GPL can be applied should you wish to distribute
	a combined work that includes FreeRTOS.org, without being obliged to provide
	the source code for any proprietary components.  See the licensing section 
	of http://www.FreeRTOS.org for full details of how and when the exception
	can be applied.

	***************************************************************************
	See http://www.FreeRTOS.org for documentation, latest information, license 
	and contact details.  Please ensure to read the configuration and relevant 
	port sections of the online documentation.

	Also see http://www.SafeRTOS.com for an IEC 61508 compliant version along
	with commercial development and support options.
	***************************************************************************
*/


/*-----------------------------------------------------------
 * Implementation of functions defined in portable.h for the ARM7 port.
 *
 * Components that can be compiled to either ARM or THUMB mode are
 * contained in this file.  The ISR routines, which can only be compiled
 * to ARM mode are contained in portISR.c.
 *----------------------------------------------------------*/

/*
	Changes from V2.5.2
		
	+ ulCriticalNesting is now saved as part of the task context, as is 
	  therefore added to the initial task stack during pxPortInitialiseStack.
*/


/* Standard includes. */
#include <stdlib.h>

/* Scheduler includes. */
#include "FreeRTOS.h"
#include "task.h"

/* Processor constants. */
#include "AT91SAM7.h"
#include "lib_AT91SAM7.h"

/* Constants required to setup the task context. */
#define portINITIAL_SPSR				( ( portSTACK_TYPE ) 0x1f )	/* System mode, ARM mode, interrupts enabled. */
#define portTHUMB_MODE_BIT				( ( portSTACK_TYPE ) 0x20 )
#define portINSTRUCTION_SIZE			( ( portSTACK_TYPE ) 4 )
#define portNO_CRITICAL_SECTION_NESTING	( ( portSTACK_TYPE ) 0 )

/* Constants required to setup the tick ISR. */
#define portENABLE_TIMER			( ( unsigned portCHAR ) 0x01 )
#define portPRESCALE_VALUE			0x00
#define portINTERRUPT_ON_MATCH		( ( unsigned portLONG ) 0x01 )
#define portRESET_COUNT_ON_MATCH	( ( unsigned portLONG ) 0x02 )

/* Constants required to setup the PIT. */
#define portPIT_CLOCK_DIVISOR			( ( unsigned portLONG ) 16 )
#define portPIT_COUNTER_VALUE			( ( ( configCPU_CLOCK_HZ / portPIT_CLOCK_DIVISOR ) / 1000UL ) * portTICK_RATE_MS )

#define portINT_LEVEL_SENSITIVE  0
#define portPIT_ENABLE      	( ( unsigned portSHORT ) 0x1 << 24 )
#define portPIT_INT_ENABLE     	( ( unsigned portSHORT ) 0x1 << 25 )
/*-----------------------------------------------------------*/

/* Setup the timer to generate the tick interrupts. */
static void prvSetupTimerInterrupt (void);

/* 
 * The scheduler can only be started from ARM mode, so 
 * vPortISRStartFirstSTask() is defined in portISR.c. 
 */
extern void vPortISRStartFirstTask (void);

/*-----------------------------------------------------------*/

/* 
 * Initialise the stack of a task to look exactly as if a call to 
 * portSAVE_CONTEXT had been called.
 *
 * See header file for description. 
 */
portSTACK_TYPE *
pxPortInitialiseStack (portSTACK_TYPE * pxTopOfStack, pdTASK_CODE pxCode,
		       void *pvParameters)
{
  portSTACK_TYPE *pxOriginalTOS;

  pxOriginalTOS = pxTopOfStack;

  /* Setup the initial stack of the task.  The stack is set exactly as 
     expected by the portRESTORE_CONTEXT() macro. */

  /* First on the stack is the return address - which in this case is the
     start of the task.  The offset is added to make the return address appear
     as it would within an IRQ ISR. */
  *pxTopOfStack = (portSTACK_TYPE) pxCode + portINSTRUCTION_SIZE;
  pxTopOfStack--;

  *pxTopOfStack = (portSTACK_TYPE) 0xaaaaaaaa;	/* R14 */
  pxTopOfStack--;
  *pxTopOfStack = (portSTACK_TYPE) pxOriginalTOS;	/* Stack used when task starts goes in R13. */
  pxTopOfStack--;
  *pxTopOfStack = (portSTACK_TYPE) 0x12121212;	/* R12 */
  pxTopOfStack--;
  *pxTopOfStack = (portSTACK_TYPE) 0x11111111;	/* R11 */
  pxTopOfStack--;
  *pxTopOfStack = (portSTACK_TYPE) 0x10101010;	/* R10 */
  pxTopOfStack--;
  *pxTopOfStack = (portSTACK_TYPE) 0x09090909;	/* R9 */
  pxTopOfStack--;
  *pxTopOfStack = (portSTACK_TYPE) 0x08080808;	/* R8 */
  pxTopOfStack--;
  *pxTopOfStack = (portSTACK_TYPE) 0x07070707;	/* R7 */
  pxTopOfStack--;
  *pxTopOfStack = (portSTACK_TYPE) 0x06060606;	/* R6 */
  pxTopOfStack--;
  *pxTopOfStack = (portSTACK_TYPE) 0x05050505;	/* R5 */
  pxTopOfStack--;
  *pxTopOfStack = (portSTACK_TYPE) 0x04040404;	/* R4 */
  pxTopOfStack--;
  *pxTopOfStack = (portSTACK_TYPE) 0x03030303;	/* R3 */
  pxTopOfStack--;
  *pxTopOfStack = (portSTACK_TYPE) 0x02020202;	/* R2 */
  pxTopOfStack--;
  *pxTopOfStack = (portSTACK_TYPE) 0x01010101;	/* R1 */
  pxTopOfStack--;

  /* When the task starts is will expect to find the function parameter in
     R0. */
  *pxTopOfStack = (portSTACK_TYPE) pvParameters;	/* R0 */
  pxTopOfStack--;

  /* The last thing onto the stack is the status register, which is set for
     system mode, with interrupts enabled. */
  *pxTopOfStack = (portSTACK_TYPE) portINITIAL_SPSR;

#ifdef THUMB_INTERWORK
  {
    /* We want the task to start in thumb mode. */
    *pxTopOfStack |= portTHUMB_MODE_BIT;
  }
#endif

  pxTopOfStack--;

  /* Some optimisation levels use the stack differently to others.  This 
     means the interrupt flags cannot always be stored on the stack and will
     instead be stored in a variable, which is then saved as part of the
     tasks context. */
  *pxTopOfStack = portNO_CRITICAL_SECTION_NESTING;

  return pxTopOfStack;
}

/*-----------------------------------------------------------*/

portBASE_TYPE
xPortStartScheduler (void)
{
  /* Start the timer that generates the tick ISR.  Interrupts are disabled
     here already. */
  prvSetupTimerInterrupt ();

  /* Start the first task. */
  vPortISRStartFirstTask ();

  /* Should not get here! */
  return 0;
}

/*-----------------------------------------------------------*/

void
vPortEndScheduler (void)
{
  /* It is unlikely that the ARM port will require this function as there
     is nothing to return to.  */
}

/*-----------------------------------------------------------*/

/*
 * Setup the timer 0 to generate the tick interrupts at the required frequency.
 */
static void
prvSetupTimerInterrupt (void)
{
  AT91PS_PITC pxPIT = AT91C_BASE_PITC;

  /* Setup the AIC for PIT interrupts.  The interrupt routine chosen depends
     on whether the preemptive or cooperative scheduler is being used. */
#if configUSE_PREEMPTION == 0

  extern void (vNonPreemptiveTick) (void);
  AT91F_AIC_ConfigureIt (AT91C_ID_SYS, AT91C_AIC_PRIOR_HIGHEST,
			 portINT_LEVEL_SENSITIVE,
			 (void (*)(void)) vNonPreemptiveTick);

#else

  extern void (vPreemptiveTick) (void);
  AT91F_AIC_ConfigureIt (AT91C_ID_SYS, AT91C_AIC_PRIOR_HIGHEST,
			 portINT_LEVEL_SENSITIVE,
			 (void (*)(void)) vPreemptiveTick);

#endif

  /* Configure the PIT period. */
  pxPIT->PITC_PIMR =
    portPIT_ENABLE | portPIT_INT_ENABLE | portPIT_COUNTER_VALUE;

  /* Enable the interrupt.  Global interrupts are disables at this point so 
     this is safe. */
  AT91C_BASE_AIC->AIC_IECR = 0x1 << AT91C_ID_SYS;
}

/*-----------------------------------------------------------*/
